package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.aggregations.TopHitsAggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {

    private static final String INDEX_NAME="albuminfo";
    private static final String SUGGEST_INDEX_NAME="suggestinfo";


    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private AlbumInfoIndexRepository repository;

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private ElasticsearchClient elasticsearchClient;
    /**
     * 专辑上架
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {

        //封装上架专辑对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        //1.封装专辑数据
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {

            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            //断言判断判断
            Assert.notNull(albumInfo, "根据ID查询专辑信息异常，专辑ID：{}", albumId);
            //拷贝数据
            BeanUtils.copyProperties(albumInfo, albumInfoIndex);

            //上架时间
            albumInfoIndex.setCreateTime(new Date());

            //获取查询的专辑属性
            List<AlbumAttributeValue> attributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            //判断
            if (CollectionUtils.isNotEmpty(attributeValueVoList)) {

                //转化专辑属性
                List<AttributeValueIndex> attributeValueIndexList = attributeValueVoList.stream().map(albumAttributeValue -> {

                    AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                    attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                    attributeValueIndex.setValueId(albumAttributeValue.getValueId());

                    return attributeValueIndex;
                }).collect(Collectors.toList());
                //设置属性操作
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);

            }


            return albumInfo;
        }, executor);
        //2.封装用户数据
        CompletableFuture<Void>  userCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {

            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(albumInfo.getUserId()).getData();
            Assert.notNull(userInfoVo, "根据userId查询用户信息异常，用户ID：{}", albumInfo.getUserId());
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());


        }, executor);

        //3.封装分类数据
        CompletableFuture<Void> categoryViewCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {

            BaseCategoryView baseCategoryView = albumFeignClient.getCategoryView(albumInfo.getCategory3Id()).getData();
            Assert.notNull(baseCategoryView, "根据三级分类ID查询分类信息异常，三级分类ID：{}", albumInfo.getCategory3Id());
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());

        }, executor);


        CompletableFuture<Void> albumStatCompletableFuture = CompletableFuture.runAsync(() -> {

            //4.封装统计数据---目前统计数据均为0，测试效果不佳，所以采用随机生成模拟数据的方式
//        AlbumStatVo albumStatVo = albumFeignClient.getAlbumStatVo(albumId).getData();
//        Assert.notNull(albumStatVo,"根据ID查询专辑统计信息异常，专辑ID：{}",albumId);


            //5.1 随机为专辑产生播放量，订阅量，购买量，评论量 、
            int num1 = RandomUtil.randomInt(1000, 2000);
            int num2 = RandomUtil.randomInt(500, 1000);
            int num3 = RandomUtil.randomInt(200, 400);
            int num4 = RandomUtil.randomInt(100, 200);
            albumInfoIndex.setPlayStatNum(num1);
            albumInfoIndex.setSubscribeStatNum(num2);
            albumInfoIndex.setBuyStatNum(num3);
            albumInfoIndex.setCommentStatNum(num4);

            //5.2 基于统计值计算出专辑得分 为不同统计类型设置不同权重
            BigDecimal bigDecimal1 = new BigDecimal(num4).multiply(new BigDecimal("0.4"));
            BigDecimal bigDecimal2 = new BigDecimal(num3).multiply(new BigDecimal("0.3"));
            BigDecimal bigDecimal3 = new BigDecimal(num2).multiply(new BigDecimal("0.2"));
            BigDecimal bigDecimal4 = new BigDecimal(num1).multiply(new BigDecimal("0.1"));
            BigDecimal hotScore = bigDecimal1.add(bigDecimal2).add(bigDecimal3).add(bigDecimal4);
            albumInfoIndex.setHotScore(hotScore.doubleValue());


        }, executor);

        //allof
        CompletableFuture.allOf(
                albumInfoCompletableFuture,
                userCompletableFuture,
                categoryViewCompletableFuture,
                albumStatCompletableFuture

        ).join();

        repository.save(albumInfoIndex);


        //上架后，需要同步题词库
        this.saveSuggestIndex(albumInfoIndex.getId(),albumInfoIndex.getAlbumTitle());

    }

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    /**
     * 同步题词库
     * @param id
     * @param albumTitle
     */
    public void saveSuggestIndex(Long id, String albumTitle) {

        //封装题词对象
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(id.toString());
        suggestIndex.setTitle(albumTitle);
        //设置关键字匹配题词
        suggestIndex.setKeyword(new Completion(new String[]{albumTitle}));
        //设置拼音匹配题词
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinyinUtil.getPinyin(albumTitle, "")}));
        //设置首字母缩写题词
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinyinUtil.getFirstLetter(albumTitle, "")}));

        suggestIndexRepository.save(suggestIndex);
    }


    /**
     * 专辑下架
     * @param albumId
     */
    @Override
    public void lowerAlbum(Long albumId) {
        repository.deleteById(albumId);
        //同步题词库
        suggestIndexRepository.deleteById(albumId.toString());
    }

    /**
     * 专辑关键字检索
     * @param query
     * @return
     */
    @Override
    @SneakyThrows
    public AlbumSearchResponseVo search(AlbumIndexQuery query) {

        //创建searchRequest对象
        SearchRequest searchRequest=this.builderDSL(query);
        System.out.println("生成的语句："+searchRequest.toString());
        //执行查询
        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        //转换类型
        AlbumSearchResponseVo albumSearchResponseVo= this.parseResponseResult(searchResponse,query);

        return albumSearchResponseVo;
    }

    /**
     * 查询指定一级分类下热门排行专辑
     * @param category1Id
     * @return
     */
    @Override
    @SneakyThrows
    public List<Map<String, Object>> channel(Long category1Id) {

        //创建封装对象
        List<Map<String,Object>> mapList=new ArrayList<>();


        //获取一级分类指定的三级分类列表
        List<BaseCategory3> baseCategory3List = albumFeignClient.findTopBaseCategory3(category1Id).getData();

        //转化list为map结构
        Map<Long, BaseCategory3> baseCategory3Map = baseCategory3List.stream().collect(Collectors.toMap(baseCategory3 -> baseCategory3.getId(), baseCategory3 -> baseCategory3));



        if(CollectionUtils.isEmpty(baseCategory3List)){
            throw new GuiguException(500,"服务器异常，请联系管理员1564562416543");
        }
        //获取三级分类id列表
//        List<Long> category3IdList = baseCategory3List.stream().map(baseCategory3 -> baseCategory3.getId()).collect(Collectors.toList());

        List<FieldValue> fieldValueList = baseCategory3List.stream().map(baseCategory3 -> FieldValue.of(baseCategory3.getId())).collect(Collectors.toList());


        //编写DSL语句,执行查询
        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(s ->
                        s.index(INDEX_NAME)
                                .query(q -> q.terms(t -> t.field("category3Id").terms(te -> te.value(fieldValueList))))
                                .size(0)
                                .aggregations("category3IdAgg", agg ->
                                        agg.terms(t -> t.field("category3Id"))
                                                .aggregations("top6", subAgg -> subAgg.topHits(to -> to.size(6)
                                                        .sort(so -> so.field(fi -> fi.field("hotScore").order(SortOrder.Desc)))))),
                AlbumInfoIndex.class);

        //解析结果
        Map<String, Aggregate> aggregations = searchResponse.aggregations();
        //获取指定的分组结果
        LongTermsAggregate category3IdAgg = aggregations.get("category3IdAgg").lterms();

        //获取buckets数据
        List<LongTermsBucket> bucketList = category3IdAgg.buckets().array();

        //判断是否有结果
        if(CollectionUtils.isNotEmpty(bucketList)){

             mapList = bucketList.stream().map(longTermsBucket -> {
                //创建Map封装数据
                Map<String, Object> resultMap = new HashMap<>();

                //获取三级分类id
                 long category3Id = longTermsBucket.key();
                //获取指定对象
                 BaseCategory3 baseCategory3 = baseCategory3Map.get(category3Id);

                 //封装三级分类对象baseCategory3
                 resultMap.put("baseCategory3",baseCategory3);

                 //获取分组专辑数据
                 TopHitsAggregate top6 = longTermsBucket.aggregations().get("top6").topHits();
                 List<Hit<JsonData>> hitList = top6.hits().hits();
                 //判断
                 if(CollectionUtils.isNotEmpty(hitList)){

                     List<AlbumInfoIndex> albumInfoIndexList=hitList.stream().map(jsonDataHit -> jsonDataHit.source().to(AlbumInfoIndex.class)).collect(Collectors.toList());

                     //封装三级分类对应的热度专辑
                     resultMap.put("list",albumInfoIndexList);

                 }




                return resultMap;

            }).collect(Collectors.toList());


        }

        return mapList;
    }

    /**
     *  关键字自动补全
     * @param keyword
     * @return
     */
    @Override
    @SneakyThrows
    public List<String> completaSuggest(String keyword) {


        //执行查询
        SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(s ->
                        s.index(SUGGEST_INDEX_NAME)
                                .suggest(su ->
                                        su.suggesters("mySuggestKeyword", sug -> sug.prefix(keyword).completion(co -> co.field("keyword").size(10).skipDuplicates(true).fuzzy(fu -> fu.fuzziness("auto"))))
                                                .suggesters("mySuggestPinyin", sug -> sug.prefix(keyword).completion(co -> co.field("keywordPinyin").size(10).skipDuplicates(true).fuzzy(fu -> fu.fuzziness("auto"))))
                                                .suggesters("mySuggestSequence", sug -> sug.prefix(keyword).completion(co -> co.field("keywordSequence").size(10).skipDuplicates(true).fuzzy(fu -> fu.fuzziness("auto"))))

                                ),
                SuggestIndex.class);

        //获取所有题词结果
        Map<String, List<Suggestion<SuggestIndex>>> suggest = searchResponse.suggest();

        //创建封装查询结果的集合
        Set<String> resultSet = new LinkedHashSet<>();
        //定义处理方法
        this.parseResultSuggest("mySuggestKeyword",suggest,resultSet);
        this.parseResultSuggest("mySuggestPinyin",suggest,resultSet);
        this.parseResultSuggest("mySuggestSequence",suggest,resultSet);

        //判断
        if(resultSet.size()>=10){

            return  new ArrayList<String>(resultSet).subList(0,10);
        }

        //模糊查询
        SearchResponse<AlbumInfoIndex> albumInfoIndexSearchResponse = elasticsearchClient.search(s -> s.index(INDEX_NAME).query(q -> q.match(m -> m.field("albumTitle").query(keyword))), AlbumInfoIndex.class);

        //解析结果
        List<Hit<AlbumInfoIndex>> hits = albumInfoIndexSearchResponse.hits().hits();
        //判断是否有值
        if(CollectionUtils.isNotEmpty(hits)){
            for (Hit<AlbumInfoIndex> hit : hits) {
                AlbumInfoIndex albumInfoIndex = hit.source();
                resultSet.add(albumInfoIndex.getAlbumTitle());
                //判断
                if(resultSet.size()==10){
                    break;
                }
            }


        }


        return  new ArrayList<String>(resultSet);
    }

    /**
     * 处理题词结果
     * @param mySuggestKeyword
     * @param suggest
     * @param resultSet
     */
    public void parseResultSuggest(String mySuggest, Map<String, List<Suggestion<SuggestIndex>>> suggest, Set<String> resultSet) {


        //获取指定的题词结果
        Suggestion<SuggestIndex> suggestIndexSuggestion = suggest.get(mySuggest).get(0);
        //获取题词数据
        List<CompletionSuggestOption<SuggestIndex>> optionList = suggestIndexSuggestion.completion().options();
        //判断
        if(CollectionUtils.isNotEmpty(optionList)){

            for (CompletionSuggestOption<SuggestIndex> suggestIndexCompletionSuggestOption : optionList) {
                SuggestIndex suggestIndex = suggestIndexCompletionSuggestOption.source();


                resultSet.add(suggestIndex.getTitle());
            }


        }



    }

    /**
     * 构建查询的DSL语句，返回请求对象
     * @param query
     * @return
     */
    private SearchRequest builderDSL(AlbumIndexQuery query) {

        //创建请求对象
        SearchRequest.Builder builder = new SearchRequest.Builder();

        //设置索引库
        builder.index(INDEX_NAME);
        //创建多条件封装对象
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        //获取关键字
        String keyword = query.getKeyword();
        //判断
        if(StringUtils.isNotEmpty(keyword)){
            boolQueryBuilder.should(s->s.match(m->m.query(keyword).field("albumTitle")));
            boolQueryBuilder.should(s->s.match(m->m.query(keyword).field("albumIntro")));
            boolQueryBuilder.should(s->s.term(t->t.value(keyword).field("announcerName")));

        }

        //三级分类条件添加
        if(query.getCategory1Id()!=null&&query.getCategory1Id()!=0){
            boolQueryBuilder.filter(f->f.term(t->t.field("category1Id").value(query.getCategory1Id())));


        }

        if(query.getCategory2Id()!=null&&query.getCategory2Id()!=0){
            boolQueryBuilder.filter(f->f.term(t->t.field("category2Id").value(query.getCategory2Id())));


        }
        if(query.getCategory3Id()!=null&&query.getCategory3Id()!=0){
            boolQueryBuilder.filter(f->f.term(t->t.field("category3Id").value(query.getCategory3Id())));


        }

        //判断属性
        List<String> attributeList = query.getAttributeList();
        //判断
        if(CollectionUtils.isNotEmpty(attributeList)){

            for (String attribute : attributeList) {
                //attribute-->属性id:属性值id
                //截取字符串
                String[] split = attribute.split(":");
                //判断
                if(split!=null&&split.length==2){

                    boolQueryBuilder.filter(f->f.nested(n->n.path("attributeValueIndexList").query(q->q.bool(b->
                            b.must(mu->mu.term(t->t.field("attributeValueIndexList.attributeId").value(split[0])))
                                    .must(mu->mu.term(t->t.field("attributeValueIndexList.valueId").value(split[1])))))));

                }

            }



        }




//        //设置查询条件
        builder.query(boolQueryBuilder.build()._toQuery());
//        //设置查询分页
        //开始的索引=（当前页-1）*每页条数
        builder.from((query.getPageNo()-1)*query.getPageSize());
        builder.size(query.getPageSize());
//        //设置查询高亮
        if(StringUtils.isNotEmpty(keyword)){//必须是关键字查询才有高亮
            builder.highlight(h->h.fields("albumTitle",hi->hi.preTags("<font color='red'>").postTags("</font>")));

        }
//        //设置排序
        String order = query.getOrder();
        //判断
        if(StringUtils.isNotEmpty(order)){
            //排序（综合排序[1:desc] 播放量[2:desc] 发布时间[3:desc]；asc:升序 desc:降序）
            //order=1:desc
            String[] split = order.split(":");
            if(split!=null&&split.length==2){
                String filed=null;
                //转换
                switch (split[0]){
                    case "1":
                        filed="hotScore";
                        break;
                    case "2":
                        filed="playStatNum";
                        break;
                    case "3":
                        filed="createTime";
                        break;
                }


                String finalFiled = filed;

                builder.sort(so->so.field(fi->fi.field(finalFiled).order("desc".equals(split[1])? SortOrder.Desc:SortOrder.Asc)));
            }

        }


//        //设置过滤字段
        builder.source(sou->sou.filter(fi->fi.excludes("category1Id",
                "category2Id",
                "category3Id",
                "attributeValueIndexList.attributeId",
                "attributeValueIndexList.valueId")));

        //构建请求对象
        return builder.build();
    }

    /**
     * 转换返回结果
     * @param searchResponse
     * @return
     */
    private AlbumSearchResponseVo parseResponseResult(SearchResponse<AlbumInfoIndex> searchResponse,AlbumIndexQuery query) {

        //创建封装结果对象
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();

        HitsMetadata<AlbumInfoIndex> hits = searchResponse.hits();
        //封装专辑列表数据
        List<Hit<AlbumInfoIndex>> albumHits = hits.hits();
        //判断
        if(CollectionUtils.isNotEmpty(albumHits)){
            //获取专辑列表数据
            List<AlbumInfoIndexVo> albumInfoIndexVoList = albumHits.stream().map(albumInfoIndexHit -> {

                AlbumInfoIndex albumInfoIndex = albumInfoIndexHit.source();

                //拷贝数据到vo
                AlbumInfoIndexVo albumInfoIndexVo = BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);
                //尝试获取高亮数据
                Map<String, List<String>> highlight = albumInfoIndexHit.highlight();
                //判断
                if(!CollectionUtils.isMapEmpty(highlight)){
                    //根据指定的key获取
                    List<String> albumTitles = highlight.get("albumTitle");
                    //判断
                    if(CollectionUtils.isNotEmpty(albumTitles)){
                        //获取高亮标题，设置覆盖
                        albumInfoIndexVo.setAlbumTitle(albumTitles.get(0));


                    }


                }


                return albumInfoIndexVo;
            }).collect(Collectors.toList());

            albumSearchResponseVo.setList(albumInfoIndexVoList);
        }






        //封装分页数据
        albumSearchResponseVo.setPageNo(query.getPageNo());
        albumSearchResponseVo.setPageSize(query.getPageSize());
        //获取总记录数据
        long total = hits.total().value();
        albumSearchResponseVo.setTotal(total);
        //计算
        long totalPages=total%albumSearchResponseVo.getPageSize()==0?total/albumSearchResponseVo.getPageSize():(total/albumSearchResponseVo.getPageSize()+1);
        albumSearchResponseVo.setTotalPages(totalPages);
        return albumSearchResponseVo;
    }
}
